/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.tewi.jeeunit.junit4;

import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import pl.tewi.jeeunit.junit4.statements.DeployApplicationResources;
import pl.tewi.junit.annotations.processors.JUnitAnnotationProcessor;
import pl.tewi.jeeunit.junit4.statements.StartServer;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.tewi.contexts.ApplicationServerContext;
import pl.tewi.exceptions.AnnotationProcessingException;
import pl.tewi.exceptions.GeneralFrameworkException;
import pl.tewi.jeeunit.junit4.statements.ConfigureSecurity;
import pl.tewi.jeeunit.junit4.statements.DeployApplication;
import pl.tewi.jeeunit.junit4.statements.StopServer;

/**
 *
 * @author Marcin Kwapisz
 */
public class JeeSuite extends Suite {

    private static final Logger LOGGER = LoggerFactory.getLogger(JeeSuite.class);
    /**
     * Przetwarza adnotacje na klasie {@link JeeSuite}
     */
//    private JUnitAnnotationProcessor annotationProcessor = new JUnitAnnotationProcessor();
    private ApplicationServerContext applicationServerContext = null;

    /**
     * Konstruktor wywoływany przez Junit. Uruchamia narzędzie z własnym builderem
     * {@link JeeUnitBlockJUnit4ClassRunner}
     * @param klass klasa zestawu testów
     * @throws InitializationError problem z inicjalizacją test runnera
     * @throws AnnotationProcessingException problem z przetwarzaniem adnotacj na klasie zestawu testów
     * @throws GeneralFrameworkException problem z działaniem narzędzia
     */
    public JeeSuite(final Class<?> klass) throws InitializationError, AnnotationProcessingException, GeneralFrameworkException {
        this(klass, new JeeUnitRunnerBuilder());
    }

    /**
     * Konstruktor tej klasy TestSuite podmienia {@link AllDefaultPossibilitiesBuilder}
     * na własny {@link JeeUnitRunnerBuilder}. Ogranicza to jednak użycie narzedzia
     * do JUnit4
     * @param klass - klasa będąca zestawem testów (test suite), musi być opisana
     * adnotacją @RunWith(JeeSuite.class)
     * @param builder komponent Junit tworzący test runnery
     * @throws InitializationError problem z inicjalizacją test runnera
     * @throws AnnotationProcessingException problem z przetwarzaniem adnotacj na klasie zestawu testów
     * @throws GeneralFrameworkException problem z działaniem narzędzia
     */
    public JeeSuite(final Class<?> klass, final RunnerBuilder builder) throws InitializationError, AnnotationProcessingException, GeneralFrameworkException {
        super(klass, builder);
        //przetwórz adnotacje znajdujące się na klass
        applicationServerContext = JUnitAnnotationProcessor.getInstance().processAnnotations(klass, null);
    }

    /**
     * Metoda wykorzystana do przekazania JUnit runnerom 
     * kontekstu serwera aplikacyjnego przed ich uruchomieniem
     * @param runner test runner klasy {@link JeeUnitBlockJUnit4ClassRunner}
     * @param notifier komponent junit odpowiedzialny z przekazanie wyników testu
     */
    @Override
    protected void runChild(final Runner runner, final RunNotifier notifier) {
        // ustaw kontekst serwera aplikacyjnego dla test runnera
        // runner musi być typu JeeUnitBlockJUnit4ClassRunner
        if (runner instanceof JeeUnitBlockJUnit4ClassRunner) {
            ((JeeUnitBlockJUnit4ClassRunner) runner).setApplictionServerContext(applicationServerContext);
        }
        super.runChild(runner, notifier);
    }

    /**
     * Konstrukcja kolejności wykonania operacji przez narzędzie testujące.
     * Statementy muszą być dodawane w odwrotnej kolejności, gdyż dodawane są już
     * przed dodanymi. Kolejność wykonania operacji:
     * <ul><li>Uruchom serwer</li><li>Zainstaluj zasoby</li><li>Konfiguracja bezpieczeństwa</li><li>Zainstaluj aplikacje</li></ul>
     * @param statement statement który zostanie uzupełniony o statements związane z cykelm życia serwa aplikacyjnego
     * @return junit statement
     */
    @Override
    protected Statement withBeforeClasses(final Statement statement) {
        LOGGER.debug("Running framework withBeforeClasses");
        //Jeżeli klasa jest opisana adnotacją server to przed befores uruchom serwer
        //serwer controller nie powinien być zapamiętywany.
        //kontrolerow moze byc wiele i trzeba je tworzyc przy pomocy fabryki 
        //na podstawie kontekstu. Nic innego nie jest potrzebne
        Statement befores = super.withBeforeClasses(statement);
        befores = new DeployApplication(befores, applicationServerContext);
        befores = new ConfigureSecurity(befores, applicationServerContext);
        befores = new DeployApplicationResources(befores, applicationServerContext);
        befores = new StartServer(befores, applicationServerContext);
        return befores;
    }

    /**
     * Konstrukacja kolejności wykonania operacji przez narzędzie testujące po
     * zakończeniu testu. Polega na zamknięciu serwera aplikacyjnego
     * @param statement
     * @return junit statement
     */
    @Override
    protected Statement withAfterClasses(final Statement statement) {
        LOGGER.debug("Running framework withAfterClasses");
        Statement afters = super.withAfterClasses(statement);
        afters = new StopServer(afters, applicationServerContext);
        return afters;
    }
}
